package com.jxpanda.infrastructure.crypto.algorithm.signer;

import com.jxpanda.infrastructure.crypto.algorithm.signer.contract.AbstractSigner;
import com.jxpanda.infrastructure.crypto.constant.AlgorithmSign;
import com.jxpanda.infrastructure.crypto.model.BinaryData;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Objects;

public class HmacSigner extends AbstractSigner {

    private final SecretKey secretKey;

    public HmacSigner(AlgorithmSign.HMAC hmac) {
        super(hmac.getAlgorithm());
        this.secretKey = hmac.generateKey();
    }

    public HmacSigner(AlgorithmSign.HMAC hmac, SecretKey secretKey) {
        super(hmac.getAlgorithm());
        this.secretKey = secretKey;
    }

    @Override
    public BinaryData sign(BinaryData contentData) {
        try {
            Mac mac = Mac.getInstance(getAlgorithm());
            mac.init(secretKey);
            return new BinaryData(mac.doFinal(contentData.bytes()));
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean verify(BinaryData contentData, BinaryData signatureData) {
        BinaryData signedData = sign(contentData);
        return MessageDigest.isEqual(signedData.bytes(), signatureData.bytes());
    }

    public static Builder builder(AlgorithmSign.HMAC hmac) {
        return new Builder(hmac);
    }

    public static class Builder {

        private final AlgorithmSign.HMAC hmac;
        private SecretKey secretKey;

        public Builder(AlgorithmSign.HMAC hmac) {
            this.hmac = hmac;
        }

        public Builder secretKey(SecretKey secretKey) {
            this.secretKey = secretKey;
            return this;
        }

        public HmacSigner build() {
            Objects.requireNonNull(secretKey, "The SecretKey must not be null.");
            return new HmacSigner(hmac, secretKey);
        }

        public HmacSigner buildDefault() {
            return new HmacSigner(hmac, hmac.generateKey());
        }
    }

}
